/*
 * Copyright (C) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use openssl::conf::Conf;

use crate::common::constants::*;
use crate::traits::config_manager::{ConfigManager, PinExpiredInfo};
use crate::traits::user_db_manager::{UserDbManagerRegistry, CredentialFilter, Credential};
use crate::traits::auth_policy_manager::{AuthPolicyManagerRegistry};
use crate::traits::storage_io::StorageIoRegistry;
use crate::traits::storage_io::StorageType;
use crate::utils::parcel;
use core::convert::TryFrom;
use crate::utils::parcel::Parcel;
use crate::{Vec, log_e, log_i};
const GLOBAL_CONFIG_FILE_NAME: &str = "globalConfigInfo";
const GLOBAL_CONFIG_VERSION_1: u32 = 1;
const GLOBAL_CONFIG_VERSION_CURRENT: u32 = 2;
pub const NO_CHECK_PIN_EXPIRED_PERIOD: i64 = 0;
pub const NO_SET_PIN_EXPIRED_PERIOD: i64 = -1;

#[derive(Default)]
pub struct PinExpiredPeriodConfig {
    pub expired_period: i64,
}

#[derive(Default)]
pub struct AuthTypeEnableStatusConfig {
    pub enable_status: bool,
}

pub enum ConfigValue {
    PinExpiredPeriod(PinExpiredPeriodConfig),
    AuthTypeEnableStatus(AuthTypeEnableStatusConfig),
}

pub struct Config {
    pub config_value: ConfigValue,
    pub auth_type: AuthType,
    pub user_ids: Vec<i32>,
}

#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(i32)]
pub enum ConfigType {
    PinExpiredPeriod = 1,
    AuthTypeEnableStatus = 2,
}

impl TryFrom<i32> for ConfigType {
    type Error = ErrorCode;
    fn try_from(value: i32) -> Result<Self, ErrorCode> {
        match value {
            1 => Ok(ConfigType::PinExpiredPeriod),
            2 => Ok(ConfigType::AuthTypeEnableStatus),
            _ => {
                log_e!("Invalid configType: {}", value);
                Err(ErrorCode::BadParam)
            }
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub struct ConfigKey {
    pub config_type: ConfigType,
    pub auth_type: Option<AuthType>,
}

impl From<&Config> for ConfigKey {
    fn from(config: &Config) -> Self {
        match &config.config_value {
            ConfigValue::PinExpiredPeriod(_value) => {
                ConfigKey {
                    config_type: ConfigType::PinExpiredPeriod,
                    auth_type: None,
                }
            },
            ConfigValue::AuthTypeEnableStatus(_value) => {
                ConfigKey {
                    config_type: ConfigType::AuthTypeEnableStatus,
                    auth_type: Some(config.auth_type),
                }
            }
        }
    }
}

pub struct DefaultConfigManager {
    global_config_vec: Vec<Config>,
}

impl DefaultConfigManager {
    pub fn new() -> Self {
        DefaultConfigManager {
            global_config_vec: Vec::new(),
        }
    }

    fn read_global_config_from_v1(&self,  parcel: &mut Parcel)->Result<Config, ErrorCode> {
        let config_type: i32 = parcel.read_i32()?;
        let config_value = match config_type.try_into()? {
            ConfigType::PinExpiredPeriod => {
                let expired_period = parcel.read_i64()?;
                ConfigValue::PinExpiredPeriod(PinExpiredPeriodConfig {expired_period})
            },
            _ => {
                log_e!("bad config type:{}", config_type);
                return Err(ErrorCode::GeneralError);
            },
        };
        Ok(Config {
            config_value: config_value,
            auth_type: AuthType::Pin,
            user_ids: Vec::new(),
        })
    }

    fn read_global_config_from_v2(&self,  parcel: &mut Parcel)->Result<Config, ErrorCode> {
        let config_type = ConfigType::try_from(parcel.read_i32()?)?;
        let config_value = match config_type {
            ConfigType::PinExpiredPeriod => {
                let expired_period = parcel.read_i64()?;
                ConfigValue::PinExpiredPeriod(PinExpiredPeriodConfig {expired_period})
            },
            ConfigType::AuthTypeEnableStatus => {
                let enable_status_value: i8 = parcel.read_i8()?;
                let enable_status = enable_status_value != 0;
                ConfigValue::AuthTypeEnableStatus(AuthTypeEnableStatusConfig { enable_status })
            },
        };
        let auth_type = AuthType::try_from(parcel.read_i32()?)?;
        let mut user_ids:Vec<i32> = Vec::new();
        let user_num = parcel.read_u32()?;
        for _ in 0..user_num {
            let user_id = parcel.read_i32()?;
            user_ids.push(user_id);
        }
    
        Ok(Config {
            config_value: config_value,
            auth_type: auth_type,
            user_ids: user_ids,
        })
    }

    fn read_global_config_from_parcel(&self, version:u32,  parcel: &mut Parcel)-> Result<Config, ErrorCode> {
        match version {
            GLOBAL_CONFIG_VERSION_1 => Ok(self.read_global_config_from_v1(parcel)?),
            GLOBAL_CONFIG_VERSION_CURRENT => Ok(self.read_global_config_from_v2(parcel)?),
            _ => {
                log_e!("invalid global config version:{}", version);
                return Err(ErrorCode::GeneralError);
            }
        }
    }

    fn save_golabal_config_vec(&self) -> Result<(), ErrorCode> {
        let mut parcel = Parcel::new();
    
        parcel.write_u32(GLOBAL_CONFIG_VERSION_CURRENT);
        parcel.write_u32(self.global_config_vec.len() as u32);

        for config_info in &self.global_config_vec {
            match &config_info.config_value {     
                ConfigValue::PinExpiredPeriod(value) => {
                    parcel.write_i32(ConfigType::PinExpiredPeriod as i32);
                    parcel.write_i64(value.expired_period);
                },
                ConfigValue::AuthTypeEnableStatus(value) => {
                    parcel.write_i32(ConfigType::AuthTypeEnableStatus as i32);
                    parcel.write_u8(value.enable_status as u8);
                },
            }
            parcel.write_i32(config_info.auth_type as i32);
            parcel.write_u32(config_info.user_ids.len() as u32);
            for user_id in &config_info.user_ids  {
                parcel.write_i32(*user_id);
            }
            }
        StorageIoRegistry::get().write(StorageType::Default, GLOBAL_CONFIG_FILE_NAME, parcel.as_slice())
    }
}

impl ConfigManager for DefaultConfigManager {

    fn set_pin_expired_period(&mut self, expired_period: i64) -> Result<(), ErrorCode> {
        let config_key:ConfigKey = ConfigKey {
            config_type: ConfigType::PinExpiredPeriod,
            auth_type: None,
        };
        if let Some(item) = self
            .global_config_vec
            .iter_mut()
            .find(|item| Into::<ConfigKey>::into(&**item) == config_key) {
                if let ConfigValue::PinExpiredPeriod(ref mut value) = item.config_value {
                    value.expired_period = expired_period;
                    self.save_golabal_config_vec()?;
                    return Ok(());
                }
                return Err(ErrorCode::GeneralError);
            };

        self.global_config_vec.push(Config {
            config_value: ConfigValue::PinExpiredPeriod(PinExpiredPeriodConfig {expired_period}),
            auth_type: AuthType::Pin,
            user_ids: Vec::new(),
        });
        self.save_golabal_config_vec()
    }

    fn get_pin_expired_period(&self) -> Result<i64, ErrorCode> {
        let config_key:ConfigKey = ConfigKey {
            config_type: ConfigType::PinExpiredPeriod,
            auth_type: None,
        };

        if let Some(item) = self
        .global_config_vec
        .iter()
        .find(|item| Into::<ConfigKey>::into(*item) == config_key) {
            if let ConfigValue::PinExpiredPeriod(ref value) = item.config_value {
                return Ok(value.expired_period);
            }
        };
    
        Ok(NO_CHECK_PIN_EXPIRED_PERIOD)
    }

    fn set_enable_status(&mut self, auth_type: AuthType, enable_status: bool, user_ids: &[i32]) -> Result<(), ErrorCode> {
        let config_key:ConfigKey = ConfigKey {
            config_type: ConfigType::AuthTypeEnableStatus,
            auth_type: Some(auth_type),
        };
        if let Some(item) = self
            .global_config_vec
            .iter_mut()
            .find(|item| Into::<ConfigKey>::into(&**item) == config_key) {
                if let ConfigValue::AuthTypeEnableStatus(ref mut value) = item.config_value {
                    value.enable_status = enable_status;
                    item.user_ids = user_ids.to_vec();
                    self.save_golabal_config_vec()?;
                    return Ok(());
                }
                return Err(ErrorCode::GeneralError);
            };

        self.global_config_vec.push(Config {
            config_value: ConfigValue::AuthTypeEnableStatus(AuthTypeEnableStatusConfig {enable_status}),
            auth_type: auth_type,
            user_ids: user_ids.to_vec(),
        });
        self.save_golabal_config_vec()
    }

    fn get_enable_status(&self, _auth_type: AuthType, user_id: i32) -> Result<bool, ErrorCode> {
        let config_key:ConfigKey = ConfigKey {
            config_type: ConfigType::AuthTypeEnableStatus,
            auth_type: Some(_auth_type),
        };

        if let Some(item) = self
        .global_config_vec
        .iter()
        .find(|item| Into::<ConfigKey>::into(*item) == config_key) {
            if let ConfigValue::AuthTypeEnableStatus(ref value) = item.config_value {
                if user_id == INVALID_USER_ID && item.user_ids.is_empty() {
                    return Ok(value.enable_status);
                }
                if item.user_ids.contains(&user_id) {
                    return Ok(value.enable_status);
                } else {
                    log_i!("enable is not set for userId:{}, return reverse enableStatus", user_id);
                    return Ok(!value.enable_status);
                }
            }
        };
        Ok(true)
    }

    fn get_pin_expired_info(&self, user_id: i32, auth_type: AuthType) -> Result<PinExpiredInfo, ErrorCode> {
        let mut output = PinExpiredInfo {
            enrolled_sys_time: 0,
            expired_period: NO_CHECK_PIN_EXPIRED_PERIOD,
        };
        let auth_policy = AuthPolicyManagerRegistry::get().get_auth_policy(auth_type)?;
        if !auth_policy.depends_on_pin() {
            log_e!("auth_type:{} not depend on pin", auth_type as i32);
            return Ok(output);
        }

        let pin_expired_period = self.get_pin_expired_period()?;
        if pin_expired_period == NO_CHECK_PIN_EXPIRED_PERIOD {
            log_i!("pin expired is not set");
            return Ok(output);
        }

        let pin_cred =  UserDbManagerRegistry::get().get_pin_credential(user_id)?;
        output.enrolled_sys_time = pin_cred.abandon_sys_time;
        output.expired_period = pin_expired_period;
        return Ok(output);
    }

    fn load_from_storage(&mut self) -> Result<(), ErrorCode> {
        self.global_config_vec.clear();
        let global_config_storage: Vec<u8> = StorageIoRegistry::get().read(StorageType::Default, GLOBAL_CONFIG_FILE_NAME)?;
        if global_config_storage.is_empty() {
            log_i!("db is empty");
            return Ok(())
        }

        let mut parcel = Parcel::from(global_config_storage);
        let version = parcel.read_u32()?;
        if version > GLOBAL_CONFIG_VERSION_CURRENT {
            log_e!("bad global config version:{} , current_version:{}", version, GLOBAL_CONFIG_VERSION_CURRENT);
            return Err(ErrorCode::GeneralError);
        }

        let config_info_num = parcel.read_u32()?;
        if config_info_num == 0 {
            log_i!("no exist global config info");
            return Ok(())
        }

        for _ in 0..config_info_num {
            let global_config = self.read_global_config_from_parcel(version, &mut parcel)?;
            self.global_config_vec.push(global_config);
        };
        Ok(())
    }

    fn clear(&self) -> Result<(), ErrorCode> {
        log_e!("not implemented");
        Err(ErrorCode::GeneralError)
    }
}
